#pragma once

#include "ReferenceObject.h"

template <class TYPE, class OBJECT_TYPE>
class Reference
{
public:
	CONSTRUCTOR Reference();
	CONSTRUCTOR Reference(const Reference<TYPE, OBJECT_TYPE>& CopyObject);
	DESTRUCTOR ~Reference();

	const Reference<TYPE, OBJECT_TYPE>& operator =(const Reference<TYPE, OBJECT_TYPE>& CopyObject);

	void SetData(TYPE NewData);
	TYPE GetData() const;
	OBJECT_TYPE GetObjectData() const;

	void Attach(ReferenceObject<TYPE, OBJECT_TYPE>& NewReferenceObject);
	void Detach();

	BOOL IsAttached() const;

protected:
	ReferenceObject<TYPE, OBJECT_TYPE>* mReferenceObject;
	Reference<TYPE, OBJECT_TYPE>* NextReference;
	Reference<TYPE, OBJECT_TYPE>* PreviousReference;

	TYPE Data;

	friend class ReferenceObject<TYPE, OBJECT_TYPE>;
};


/// Constructor
template <class TYPE, class OBJECT_TYPE>
Reference<TYPE, OBJECT_TYPE>::Reference()
{
	mReferenceObject = NULL;
	NextReference = NULL;
	PreviousReference = NULL;
}


/// Copy constructor
template <class TYPE, class OBJECT_TYPE>
Reference<TYPE, OBJECT_TYPE>::Reference(const Reference<TYPE, OBJECT_TYPE>& CopyObject)
{
	mReferenceObject = NULL;
	NextReference = NULL;
	PreviousReference = NULL;

	if(CopyObject.IsAttached()) Attach(*CopyObject.GetObjectData());
}


/// Destructor
template <class TYPE, class OBJECT_TYPE>
Reference<TYPE, OBJECT_TYPE>::~Reference()
{
	Detach();
}


/// Assignment operator
template <class TYPE, class OBJECT_TYPE>
const Reference<TYPE, OBJECT_TYPE>& Reference<TYPE, OBJECT_TYPE>::operator =(const Reference<TYPE, OBJECT_TYPE>& CopyObject)
{
	Detach();

	if(CopyObject.IsAttached()) Attach(*CopyObject.ReferenceObject);

	return (*this);
}


/// Sets a new data
template <class TYPE, class OBJECT_TYPE>
void Reference<TYPE, OBJECT_TYPE>::SetData(TYPE NewData)
{
	Data = NewData;
}


/// Returns the data
template <class TYPE, class OBJECT_TYPE>
TYPE Reference<TYPE, OBJECT_TYPE>::GetData() const
{
	return Data;
}


/// Returns the object data
template <class TYPE, class OBJECT_TYPE>
OBJECT_TYPE Reference<TYPE, OBJECT_TYPE>::GetObjectData() const
{
	return ReferenceObject->GetData();
}


/// Attaches the reference to a reference object
template <class TYPE, class OBJECT_TYPE>
void Reference<TYPE, OBJECT_TYPE>::Attach(ReferenceObject<TYPE, OBJECT_TYPE>& NewReferenceObject)
{
	Detach();

	ReferenceObject = &NewReferenceObject;
	ReferenceObject->Add(this);
}


/// Detaches the reference from its reference object
template <class TYPE, class OBJECT_TYPE>
void Reference<TYPE, OBJECT_TYPE>::Detach()
{
	if(mReferenceObject != NULL)
	{
		mReferenceObject->Remove(this);
		mReferenceObject = NULL;
	}

	NextReference = NULL;
	PreviousReference = NULL;
}


/// Checks if the reference is attached
template <class TYPE, class OBJECT_TYPE>
BOOL Reference<TYPE, OBJECT_TYPE>::IsAttached() const
{
	return (mReferenceObject != NULL);
}